﻿using System.Collections.ObjectModel;
using System.Linq;
using System.Runtime.Caching;
using System.Text;
using Newtonsoft.Json.Linq;
using RssToolkit.Rss;
using Twitterizer.Entities;

namespace BlogsAPI.Sites
{
    using System.IO;
    using System.Net;
    using System.Diagnostics;
    using System.Web;
    using Shorteners;
    using System;
    using System.Collections.Generic;
    using System.Text.RegularExpressions;
    using Abstract;
    using Interfaces;
    using System.Threading;
    using Twitterizer;
    

    /// <summary>
    /// Twitter is a real-time information network that connects you to the latest information about what you find interesting. Simply find the public streams you find most compelling and follow the conversations.
    /// At the heart of Twitter are small bursts of information called Tweets. Each Tweet is 140 characters in length, but don’t let the small size fool you—you can share a lot with a little space. Connected to each Tweet is a rich details pane that provides additional information, deeper context and embedded media. You can tell your story within your Tweet, or you can think of a Tweet as the headline, and use the details pane to tell the rest with photos, videos and other media content. See it in action.
    /// </summary>
    [BlogServiceAPI(Description = "http://twitter.com", IsMicroblog = true)]
    public class Twitter : BlogServer, IPosts, IFriends, IQuote, IData, ILike
    {
        /// <summary>
        /// Для аутентификации, когда нет ничего
        /// </summary>
        public static readonly UserAccount oSelf = new UserAccount(){username = "blogsapi", password = "qwe123asd"};

        private static readonly Regex Validation = new Regex(@"^https?://(?:\w+\.)?twitter\.com", Regexoptions);
        private static readonly Regex ValidationFull = new Regex(@"^https?://(?:[-_\w]+\.)?twitter\.com/(?:#!/)?([^#!]+?)(?:/|$)", Regexoptions);
        
        private static readonly Regex rStatusId = new Regex(@"s/(\d+)\b", Regexoptions);
        private static readonly Regex rAuthToken = new Regex(@"<input.*?name\W+authenticity_token.+?value\W+(\w+)\W", Regexoptions);
        private static readonly Regex rAuthPin = new Regex(@"\Woauth_pin\W\D+(\d+)\D", Regexoptions);

        /// <summary>
        /// Название приложения на сайте http://dev.twitter.com
        /// </summary>
        public static string appName = "BlogsAPI";

        /// <summary>
        /// Где находятся настройки для твиттера
        /// </summary>
        public string DefaultConfig;

        #region interface: BlogServer

        /// <summary>
        /// Конструктор!
        /// </summary>
        [DebuggerStepThrough]
        public Twitter():this(Configuration.DefaultFile)
        {
        }

        /// <summary>
        /// Конструктор!
        /// </summary>
        /// <param name="cfg">Где файл с токенами</param>
        [DebuggerStepThrough]
        public Twitter(string cfg)
        {
            DefaultConfig = cfg;
        }

        /// <summary>
        /// http://www.t30p.ru/Twitter.aspx
        /// </summary>
        public override bool HasRating
        {
            get
            {
                return true;
            }
        }

        /// <summary>
        /// проверка, является ли передаваемый урл чатью этого сервиса
        /// </summary>
        public override Boolean ValidateLink(String sLink)
        {
            if (String.IsNullOrEmpty(sLink)) return false;
            return Validation.Match(sLink).Success;
        }


        /// <summary>
        /// Получение ссылки на блог блогера
        /// </summary>
        /// <param name="userName">ник блогера</param>
        public override String GetBlogLink(string userName)
        {
            return String.Format("https://twitter.com/{0}", userName.ToLower());
        }

        /// <summary>
        /// проверка, является ли передаваемый урл чатью этого сервиса
        /// </summary>
        /// <param name="sLink">ссылка для проверки</param>
        /// <param name="userName">Имя пользователя на блогохостинге</param>
        /// <param name="bType">Тип блога - сообщество или блог</param>
        public override Boolean ValidateLink(String sLink, out String userName, out BlogType bType)
        {
            bType = BlogType.Unknown;
            userName = String.Empty;
            if (String.IsNullOrEmpty(sLink)) return false;
            Match m = ValidationFull.Match(sLink);
            if (m.Success)
            {
                var oSystem = new List<string> { "home", "share", "status", "statuses" };
                if (oSystem.Contains(m.Groups[1].Value.ToLower()))
                {
                    bType = BlogType.Unknown;//системное имя
                    return false;
                }
                else
                {
                    bType = BlogType.User;
                    userName = m.Groups[1].Value;
                }
            }
            return m.Success;
        }

        /// <summary>
        /// Унифицированная ссылока с HTTPS!
        /// </summary>
        /// <param name="link"></param>
        /// <returns></returns>
        public override String ToCanonicalLink(string link)
        {
            if (String.IsNullOrWhiteSpace(link)) return String.Empty;
            link = link.Replace("http://", "https://");//убираем https по умолчанию
            return link.Split(new[] { "utm_source=", "#" }, 2, StringSplitOptions.None)[0];
        }

        /// <summary>
        /// Получение ссылки на блог блогера
        /// </summary>
        /// <param name="userName">ник блогера</param>
        /// <param name="btype">Тип блога - сообщество или блог</param>
        public override String GetBlogLink(string userName, BlogType btype)
        {
            return GetBlogLink(userName);                
        }

        /// <summary>
        /// Получение номера твита по ссылке
        /// </summary>
        /// <param name="tweet">ссылка на твит</param>
        /// <returns></returns>
        public decimal GetStatusId(Uri tweet)
        {
            decimal iRet = 0;
            Match m = rStatusId.Match(tweet.OriginalString);
            if(m.Success)
            {
                iRet = decimal.Parse(m.Groups[1].Value);
            }
            return iRet;
        }

        /// <summary>
        /// Получение нормализированной ссылки на твит
        /// </summary>
        /// <param name="username">имя пользователя</param>
        /// <param name="statusid">номер твита</param>
        /// <returns></returns>
        public string GetTwitLink(string username, decimal statusid)
        {
            if (String.IsNullOrEmpty(username))
            {
                throw new ArgumentNullException("username");
            }
            if (statusid == 0)
            {
                throw new ArgumentNullException("statusid");
            }
            return String.Format("{0}/status/{1}",GetBlogLink(username),statusid);
        }

        /// <summary>
        /// Получение одного поста просто по ссылке
        /// </summary>
        /// <param name="linkOnPost">ссылка на пост</param>
        /// <remarks>делает разбор ссылки</remarks>
        public override BlogPost GetOnePost(string linkOnPost)
        {
            TwitterResponse<TwitterStatus> res;
            return GetOnePost(linkOnPost, out res);
        }

        /// <summary>
        /// Получение одного поста просто по ссылке
        /// </summary>
        /// <param name="linkOnPost">ссылка на пост</param>
        /// <remarks>делает разбор ссылки</remarks>
        public BlogPost GetOnePost(string linkOnPost, out TwitterResponse<TwitterStatus> result)
        {
            string uname;
            BlogType bt;
            result = null;
            if (ValidateLink(Common.FixHttp(linkOnPost), out uname, out bt))
            {
                return GetOnePost(linkOnPost, uname, bt, out result);
            }
            return null;
        }

        /// <summary>
        /// Извлечение html кода Img аватарки для блога.
        /// </summary>
        /// <param name="userName">ник блогера</param>
        /// <param name="btype">Тип блога - сообщество или блог</param>
        /// <returns></returns>
        public override string ExtractAvatar(string userName, BlogType btype)
        {
            String sAva = String.Empty;
            try
            {
                var isResetDate = MemoryCache.Default["RateLimited"];
                if (isResetDate != null)
                {
                    //еще не наступило время ресета
                    return null;
                }
                if(LoggedInAs == null && !LogInto(oSelf))
                {
                    return null;
                }
                OAuthTokens tokens = Configuration.GetTokens(LoggedInAs.username, DefaultConfig);
                TwitterResponse<TwitterUserCollection> oRet = TwitterUser.Lookup(tokens, new LookupUsersOptions()
                                                                                   {
                                                                                       ScreenNames = new Collection<string> {userName}
                                                                                   });
                if (oRet.Result == RequestResult.Success)
                {
                    sAva = oRet.ResponseObject[0].ProfileImageLocation;
                }
                else if (oRet.Result == RequestResult.RateLimited)
                {
                    MemoryCache.Default.Set("RateLimited", 1,
                        oRet.ResponseObject.RateLimiting.ResetDate);
                }
                else if(oRet.Result != RequestResult.FileNotFound)//deleted or baned
                {
                    Debug.Write(oRet);
                }
            }
            catch (Exception e1)
            {
                Debug.WriteLine("Ошибка загрузки аватарки блога: " + e1.Message);
            }
            return sAva;
        }

        /// <summary>
        /// Получение числа подписчиков на блог, при условии, что мы залогинились предварительно!
        /// </summary>
        /// <param name="username">Имя блогера</param>
        /// <param name="bt"></param>
        /// <returns></returns>
        public override uint? HowManyReaders(string username, BlogType bt)
        {
            //либо есть пользователь , либо заходим под собою
            if(LoggedInAs == null && !LogInto(oSelf))
            {
                return null;
            }
            if(FriendList==null || String.Compare(username,LoggedInAs.username,true)!=0)
            {
                OAuthTokens tokens = Configuration.GetTokens(LoggedInAs.username, DefaultConfig);
                FriendList =
                        TwitterFriendship.FollowersIds(tokens, new UsersIdsOptions { ScreenName = username }).ResponseObject;
            }
            if(FriendList!=null)
            {
                return (uint)FriendList.Count;
            }
            return null;
        }

        /// <summary>
        /// Определение числа комментариев к записи
        /// </summary>
        /// <param name="bp">Пост для которого считаем комменты</param>
        /// <returns>Число комментариев к посту</returns>
        public override int HowManyComments(BlogPost bp)
        {
            return HowManyComments(GetStatusId(bp.Link));
        }

        /// <summary>
        /// Основная функция получения числа ответов на твит
        /// </summary>
        /// <param name="statusid">номер статуса</param>
        /// <returns>Число комментариев к посту</returns>
        [Obsolete("Not supported at API 1.1 https://dev.twitter.com/discussions/11292")]
        public int HowManyComments(decimal statusid)
        {
            return 0;
            /*
            if (statusid == 0) return 0;
            int iRet = 0;
            try
            {
                if (LoggedInAs == null && !LogInto(oSelf))
                {
                    return 0;
                }
                OAuthTokens tokens = Configuration.GetTokens(LoggedInAs.username);
                TwitterResponse<TwitterRelatedTweetsCollection> trtc = 
                    TwitterStatus.RelatedResultsShow(tokens, statusid);
                if (trtc.Result == RequestResult.Success)
                {
                    foreach (TwitterRelatedTweets relatedTweets in trtc.ResponseObject)
                    {
                        if (relatedTweets.ResultType.Contains("weet"))
                        {
                            iRet = relatedTweets.Results.Count;
                            break;
                        }
                    }
                }
                else
                {
                    Log("Twitter GetCommentsCount failed: " + trtc.Result + " " + statusid);
                }
            }
            catch (Exception e1)
            {
                Log("Twitter GetCommentsCount failed: " + e1.Message + " " + statusid);
            }
            return iRet;
            */
        }

        /// <summary>
        /// Основная функция получения числа ретвитов
        /// </summary>
        /// <param name="statusid">номер статуса</param>
        /// <returns>Число ретвитов к посту</returns>
        public int HowManyLinks(decimal statusid)
        {
            DateTime date;
            return HowManyLinks(statusid, out date);
        }

        /// <summary>
        /// Основная функция получения числа ретвитов
        /// </summary>
        /// <param name="statusid">номер статуса</param>
        /// <param name="pubdate">дата создания твита</param>
        /// <returns>Число ретвитов к посту</returns>
        public int HowManyLinks(decimal statusid, out DateTime pubdate)
        {
            pubdate = DateTime.MinValue;
            if (statusid == 0) return 0;
            int iRet = 0;
            try
            {
                if (LoggedInAs == null && !LogInto(oSelf))
                {
                    return 0;
                }
                OAuthTokens tokens = Configuration.GetTokens(LoggedInAs.username, DefaultConfig);
                TwitterResponse<TwitterStatus> trts =
                    TwitterStatus.Show(tokens, statusid);
                if (trts.Result == RequestResult.Success)
                {
                    iRet = trts.ResponseObject.RetweetCount.HasValue ? trts.ResponseObject.RetweetCount.Value : 100;
                    pubdate = trts.ResponseObject.CreatedDate;
                }
                else
                {
                    Log("Twitter GetCommentsCount failed: " + trts.Result + " " + statusid);
                }
            }
            catch (Exception e1)
            {
                Log("Twitter GetCommentsCount failed: " + e1.Message + " " + statusid);
            }
            return iRet;
        }

        /// <summary>
        /// Получение одного поста по ссылке
        /// </summary>
        /// <param name="linkOnPost">link on Twit</param>
        /// <param name="userName">username</param>
        /// <param name="btype">usertype</param>
        /// <returns></returns>
        public override BlogPost GetOnePost(string linkOnPost, string userName, BlogType btype)
        {
            TwitterResponse<TwitterStatus> res;
            return GetOnePost(linkOnPost, userName, btype, out res);
        }

        /// <summary>
        /// Получение одного поста по ссылке
        /// </summary>
        /// <param name="linkOnPost">link on Twit</param>
        /// <param name="userName">username</param>
        /// <param name="btype">usertype</param>
        /// <returns></returns>
        public BlogPost GetOnePost(string linkOnPost, string userName, BlogType btype, out TwitterResponse<TwitterStatus> trts)
        {
            BlogPost bpRet = null;
            trts = null;
            try
            {
                OAuthTokens tokens = null;
                if(LoggedInAs != null || LogInto(Twitter.oSelf))
                {
                    tokens = Configuration.GetTokens(LoggedInAs.username, DefaultConfig); //OAuth request has limit for 350 istead of 150
                }
                decimal statusid = GetStatusId(new Uri(linkOnPost));
                trts = TwitterStatus.Show(tokens, statusid);
                if (trts.Result == RequestResult.Success)
                {
                    bpRet = GetOnePost(trts.ResponseObject, true);
                }
                else
                {
                    Log("Twitter GetOnePost failed: " + trts.Result + " " + statusid);
                }
            }
            catch (Exception e1)
            {
                Log("Twitter GetOnePost failed: " + e1.Message + " " + linkOnPost);
            }
            return bpRet;
        }

        /// <summary>
        /// Получение одного поста из Твиттер результатов
        /// </summary>
        /// <param name="status">твит</param>
        /// <returns></returns>
        public BlogPost GetOnePost(TwitterStatus status, bool loadPreview = false)
        {
            var bpRet = new BlogPost(new Uri(GetTwitLink(status.User.ScreenName, status.Id)))
            {
                Existance = true,
                Published = status.CreatedDate,
                Links = status.RetweetCount ?? 100
            };
            //считаем ретвиты и лайки за просмотры
            if (status.RetweetCount.HasValue)
            {
                bpRet.Views += status.RetweetCount.Value;
            }
            if (status.FavoriteCount.HasValue)
            {
                bpRet.Views += status.FavoriteCount.Value;
            }
            string MediaLinked = String.Empty;
            string text = status.Text;
            if (status.Entities != null)
            {
                Periscope ps = new Periscope();
                //обработка ссылок
                foreach (TwitterEntity entry in status.Entities)
                {
                    if (entry.GetType() == typeof (TwitterUrlEntity))
                    {
                        var url = entry as TwitterUrlEntity;
                        if(String.IsNullOrEmpty(url.ExpandedUrl)) continue;
                        var shrt = loadPreview ? Common.GetShortenerByLink(url.ExpandedUrl): null;
                        if(shrt != null)
                        {
                            url.ExpandedUrl = shrt.ConvertDataTo(url.ExpandedUrl, ItemType.ShortUrl, ItemType.FullUrl) ?? url.ExpandedUrl;
                            shrt = Common.GetShortenerByLink(url.ExpandedUrl);
                            if (shrt != null)
                            {
                                url.ExpandedUrl = shrt.ConvertDataTo(url.ExpandedUrl, ItemType.ShortUrl, ItemType.FullUrl) ?? url.ExpandedUrl;
                            }
                        }
                        //ищем картинку, только если нужна превьюшка(!)
                        var imager = loadPreview ? Common.GetImagerByLink(url.ExpandedUrl) : null;
                        if (imager != null)
                        {
                            MediaLinked = imager.GetThumb(url.ExpandedUrl);
                            //if(!String.IsNullOrEmpty(imager.MediaID))
                            //{
                            //    Уже не актуально, так как не можем индексировать инстаграм
                            //    bpRet.SpecialParam = imager.MediaID;//запоминаем номер картинки для индексирования(!)
                            //}
                            text = text.Replace(url.Url,
                                                String.Format("{0}<a href=\"{0}\"><img src=\"{1}\"/></a>",
                                                              url.ExpandedUrl, MediaLinked));
                        }
                        else if (ps.ValidateLink(url.ExpandedUrl))//есть трансляция из перископа
                        {
                            var video = ps.GetPost(url.ExpandedUrl);
                            if (!String.IsNullOrEmpty(video?.Video?.ImageUrlSmall))
                            {
                                text += String.Format("<img src=\"{0}\"/>", video.Video.ImageUrlSmall); //превьюшка
                            }
                        }else if (url.ExpandedUrl.Contains("/go.nike.com/"))//ссылка на гео-пробежку, нужны координаты
                        {
                            GetNikeCom(url.ExpandedUrl, ref bpRet);
                        }
                        else
                        {
                            if (url.ExpandedUrl.Contains("%"))
                            {
                                url.ExpandedUrl = HttpUtility.UrlDecode(url.ExpandedUrl);
                            }
                            //но разворачивание сократителей не делаем, так как у нас время ограничено
                            text = text.Replace(url.Url, url.ExpandedUrl);
                            if (url.ExpandedUrl.EndsWith(".jpg") || url.ExpandedUrl.EndsWith(".gif") ||
                                url.ExpandedUrl.EndsWith(".png"))
                            {
                                //не добавляем медиа, так как картинка не маленькая, а большая тут.
                                //MediaLinked = url.ExpandedUrl;
                                text += String.Format("<img src=\"{0}\"/>", url.ExpandedUrl); //картинко
                            }
                            else if(loadPreview && (status.ExtendedEntities == null || status.ExtendedEntities.Count(en=>en is TwitterMediaEntity)==0))
                                //ссылка на какой-то сайт, то попробуем извлечь превьюшку, если этой превьюшки нет в расширенной информации
                            {
                                if (status.QuotedStatus != null &&
                                    status.QuotedStatus.Entities.Any(en=>en is TwitterUrlEntity))//цитируем другой твит, а там ссылка на сайт, то берем его(!)
                                {
                                    url = status.QuotedStatus.Entities.First(en => en is TwitterUrlEntity) as TwitterUrlEntity;
                                }
                                var image = base.GetOGPreviewImage(url.ExpandedUrl);
                                if (!String.IsNullOrEmpty(image))
                                {
                                    text += String.Format("<img src=\"{0}\"/>", image); //картинко
                                    //так как уже превьюшка, то сразу ее в параметры(!)
                                    if (image.Contains("i.ytimg.com"))
                                    {
                                        //ТОЛЬКО для ЮТУБА, иначе картинка может быть очень большой
                                        MediaLinked = image;
                                    }
                                }
                            }
                        }
                    }
                    else if (entry.GetType() == typeof (TwitterMediaEntity))
                    {
                        var media = entry as TwitterMediaEntity;
                        if (media.MediaType == TwitterMediaEntity.MediaTypes.Photo)
                        {
                            text = text.Replace(media.Url,
                                                String.Format("{1}<a href=\"{0}\"><img src=\"{1}\"/></a>",
                                                              media.ExpandedUrl, media.MediaUrl));
                        }
                    }
                }
                if (status.ExtendedEntities != null && status.ExtendedEntities.Count > 1)
                {
                    //Дело в том, что первая картинка в расширенных совпадает с основной картинкой в status.Entities, поэтому Skip(1)
                    foreach (TwitterMediaEntity media in status.ExtendedEntities.Where(entry => entry is TwitterMediaEntity).Skip(1).Take(3))
                    {
                        if (media.MediaType == TwitterMediaEntity.MediaTypes.Photo)
                        {
                            text += String.Format(" <a href=\"{0}\"><img src=\"{1}\"/></a>", media.ExpandedUrl, media.MediaUrl);
                        }
                    }
                }
            }
            if (status.Geo != null && status.Geo.Coordinates != null &&
                status.Geo.Coordinates.Count>0 &&
                (status.Geo.ShapeType == TwitterGeoShapeType.Point ||
                 status.Geo.ShapeType == TwitterGeoShapeType.CircleByCenterPoint))
            {
                bpRet.SetParam(PostParams.Location, String.Format("{0} {1}", 
                    status.Geo.Coordinates[0].Latitude, status.Geo.Coordinates[0].Longitude));
            }
            if (status.InReplyToStatusId.HasValue)
            {
                //комментарий значит
                bpRet.SetParam(PostParams.Tags, status.InReplyToStatusId.Value);
            }
            bpRet.Body = text;
            if (!String.IsNullOrEmpty(MediaLinked))
            {
                //предпросмотр если есть
                bpRet.SetParam(PostParams.MediaThumbnail, new Media { url = MediaLinked });
            }
            return bpRet;
        }

        private static Regex rNikeCoord = new Regex(@"""(latitude|longitude)\D+([\d\.-]+)\D", Regexoptions); 

        /// <summary>
        /// Получение координат из Nike
        /// </summary>
        /// <param name="expandedUrl"></param>
        /// <param name="bp"></param>
        private void GetNikeCom(string expandedUrl, ref BlogPost bpRet)
        {
            try
            {
                //"latitude":54.727077,"longitude":20.484116
                using (var client = new WebClient())
                {
                    var html = client.DownloadString(expandedUrl);
                    //ищем координаты
                    var mc = rNikeCoord.Matches(html);
                    if (mc.Count == 2)
                    {
                        bpRet.SetParam(PostParams.Location, String.Format("{0} {1}",
                            mc.Cast<Match>().First(i => i.Groups[1].Value == "latitude").Groups[2].Value,
                            mc.Cast<Match>().First(i => i.Groups[1].Value == "longitude").Groups[2].Value));
                    }
                }
            }
            catch (Exception e1)
            {
                Debug.Write(e1);
            }
        }

        #endregion

        #region interface: IPosts

        /// <summary>
        /// список друзей, мы его грузим в качестве проверки, что у пользователя есть права...
        /// </summary>
        public UserIdCollection FriendList;

        /// <summary>
        /// Простая попытка залогиниться
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        public override bool LogInto(UserAccount user)
        {
            return LogInto(user, null, null);
        }

        /// <summary>
        /// Простая попытка залогиниться
        /// </summary>
        /// <param name="user"></param>
        /// <param name="accessToken">токен</param>
        /// <param name="accessTokenSecret">закрытый токен</param>
        /// <returns></returns>
        public  bool LogInto(UserAccount user, string accessToken, string accessTokenSecret = null)
        {
            try
            {
                if (!String.IsNullOrEmpty(accessToken) && !String.IsNullOrEmpty(accessTokenSecret))
                {
                    Configuration.AddTokensCache(user.username, accessToken, accessTokenSecret);
                }
                OAuthTokens tokens = Configuration.GetTokens(user.username, DefaultConfig);
                if(String.IsNullOrEmpty(tokens.AccessToken))
                {
                    //нет ключа, производим аутентификацию.
                    //залогинимся в твиттер, получим куку и передадим ее дальше
                    
                    OAuthTokenResponse response = OAuthUtility.GetRequestToken(tokens.ConsumerKey,
                        tokens.ConsumerSecret, "oob");
                    
                    HttpWebRequest myHttpWebRequest = (HttpWebRequest)
                        WebRequest.Create(OAuthUtility.BuildAuthorizationUri(response.Token));
                    myHttpWebRequest.Method = "GET";
                    //Cant work w/o Expect100Continue = false in config 
                    myHttpWebRequest.ServicePoint.Expect100Continue = false;
                    myHttpWebRequest.AllowAutoRedirect = false;
                   // myHttpWebRequest.Headers.Add(HttpRequestHeader.Cookie, sCookie);
                    HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
                    string result = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding).ReadToEnd();
                    sCookie = CookieParse(myHttpWebResponse.Headers[HttpResponseHeader.SetCookie]);
                    myHttpWebResponse.Close();
                    ///////////////////////
                    Match m = rAuthToken.Match(result);
                    if (!m.Success) throw new Exception("Twitter authenticity_token problems!");
                    myHttpWebRequest = MakeRequest("https://twitter.com/oauth/authorize");
                    //myHttpWebRequest.AllowAutoRedirect = false;
                    myHttpWebRequest.Method = "POST";
                    myHttpWebRequest.Referer = "http://twitter.com";
                    myHttpWebRequest.ContentType = "application/x-www-form-urlencoded";
                    string sQueryString =
                        String.Format(
                            "authenticity_token={0}&session%5Busername_or_email%5D={1}&session%5Bpassword%5D={2}&oauth_token={3}&redirect_after_login=/oauth/authorize?oauth_token={3}", 
                    m.Groups[1].Value,
                    user.username,
                    user.password,
                    response.Token
                            );
                    //***************//
                    byte[] byteArr = _encoding.GetBytes(sQueryString);
                    myHttpWebRequest.ContentLength = byteArr.Length;
                    myHttpWebRequest.GetRequestStream().Write(byteArr, 0, byteArr.Length);
                    /////////////////////////
                    //POSTDATA=authenticity_token=58dc284748ba864aacde7a00a414d4653e896bcf&
                    //return_to_ssl=false&session%5Busername_or_email%5D=f1ashr&session%5Bpassword%5D=qwe00qwe&remember_me=1&q=
                    myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse();
                    sCookie = CookieParse(myHttpWebResponse.Headers[HttpResponseHeader.SetCookie]);
                    result = new StreamReader(myHttpWebResponse.GetResponseStream(), Encoding).ReadToEnd();
                    myHttpWebResponse.Close();
                    //в теории отсюда нужно извлечь PIN для вставки
                    m = rAuthPin.Match(result);
                    if(!m.Success) throw new Exception("не удалось получить PIN!");

                    response = OAuthUtility.GetAccessToken(tokens.ConsumerKey,
                                                tokens.ConsumerSecret, response.Token, m.Groups[1].Value);
                    if (response != null)
                    {
                        try
                        {
                            Configuration.SetTokens(user.username, response, DefaultConfig);
                        }catch(Exception e0)
                        {
                            Log("Twitter OAuth " + e0.Message);
                        }
                    }
                }
                LoggedInAs = user;
                //Log("Twitter uses only OAuth2.0 authentification. Take care.");
                return true;
            }catch(Exception e1)
            {
                Log("Twitter OAuth "+e1.Message);
                return false;
            }
        }

        /// <summary>
        /// Выкладывание записи в блоге
        /// </summary>
        /// <param name="user">Аккаунт блогера</param>
        /// <param name="bp">пост для блога</param>
        /// <remarks>No need to provide password in user field</remarks>
        /// <returns>ссылеу на новый статус, если успешно</returns>
        public override PostResult MakePost(UserAccount user, BlogPost bp)
        {
            try
            {
                if(!LogInto(user))
                {
                    throw new Exception("Cant login to Twitter.");
                }
                OAuthTokens tokens = Configuration.GetTokens(user.username, DefaultConfig);
                var suo = new StatusUpdateOptions();
                if (!String.IsNullOrEmpty(bp.Subject))
                {
                    decimal dec;
                    if(decimal.TryParse(bp.Subject,out dec))
                    {
                        suo.InReplyToStatusId = dec;
                    }
                }
                byte[] mediaData = null;
                var media = bp.GetParam(PostParams.MediaThumbnail);
                if (media != null && media.ToString() != "")
                {
                    try
                    {
                        using(var client = new WebClient())
                        {
                            mediaData = client.DownloadData(media.ToString());
                        }
                    }catch(Exception e1)
                    {
                        Debug.Write(e1);
                    }
                }
                TwitterResponse<TwitterStatus> newStatus = mediaData == null ?
                    TwitterStatus.Update(tokens, bp.Body, suo) :
                    TwitterStatus.UpdateWithMedia(tokens, bp.Body, mediaData, suo);
                if (newStatus.Result != RequestResult.Success && 
                    newStatus.ResponseObject == null)
                {
                    throw new TwitterizerException("MakePost returns " + newStatus.Result);
                }
                return new PostResult {sLink = 
                    new Uri(GetTwitLink(user.username,newStatus.ResponseObject.Id))};
            }
            catch (TwitterizerException e1)
            {
                return new PostResult { Error = e1 };
            }
        }

        #endregion

        #region interface: IFriends

        /// <summary>
        /// Определяем сколько френдов из списка есть в наших друзьях.
        /// Для сообщества - сколько людей из списка могут писать в сообщество
        /// </summary>
        /// <param name="userName">имя блога или сообщества</param>
        /// <param name="bt">Блог или Сообщество</param>
        /// <param name="friends">список друзей</param>
        /// <returns>Список тех, что читает блог или является членами сообщества</returns>
        public override List<string> IsFriended(String userName, BlogType bt, params string[] friends)
        {
            List<string> myfriends = new List<string>(0);
            try
            {
                if (LoggedInAs == null)
                {
                    throw new Exception("Смотреть списки друзей могут только залогиненные пользователи!");
                }
                OAuthTokens tokens = Configuration.GetTokens(userName, DefaultConfig);
                if (FriendList == null)//не подгрузили , так загрузим
                {
                    //FriendsOptions options = new FriendsOptions();
                    //FriendList = TwitterFriendship.Friends(tokens,options).ResponseObject;
                    FriendList =
                        TwitterFriendship.FriendsIds(tokens, new UsersIdsOptions {ScreenName = userName}).ResponseObject;
                }
                //делаем проверку списка друзей из имен в номера
                TwitterUserCollection FriendUsers = TwitterUser.Lookup(tokens,
                    new LookupUsersOptions { ScreenNames = new Collection<string>(friends)}).ResponseObject;
                foreach (decimal tu in FriendList)
                {
                    foreach (TwitterUser friend in FriendUsers)
                    {
                        if(friend.Id == tu)
                        {
                            myfriends.Add(friend.ScreenName);
                        }
                    }
                }
            }catch(TwitterizerException e1){
                Log(e1.Message);
            }
            return myfriends;
        }

        /// <summary>
        /// Интерфейс по добавлению френдов
        /// </summary>
        /// <param name="who">Имя аккаунта для которого меняем френдов</param>
        /// <param name="friends">Набор имен аккаунтов для френдования</param>
        /// <returns>NULL, если все хорошо, иначе ошибку ввиде Exception</returns>
        public override List<String> AddFriends(UserAccount who, params string[] friends)
        {
            var oRet = new List<string>(0);
            try
            {
                if (!LogInto(who))
                {
                    throw new Exception("Cant get oauth token!");
                }
                OAuthTokens tokens = Configuration.GetTokens(who.username, DefaultConfig);
              /*  Twitterizer.Framework.Twitter tw = new Twitterizer.Framework.Twitter(who.username, who.password, "BlogsAPI");*/
                foreach (string t in friends)
                {
                    try
                    {
                        TwitterResponse<TwitterUser> usr = TwitterFriendship.Create(tokens, t);
                        if(usr == null || usr.Result != RequestResult.Success)
                        {
                            throw new TwitterizerException("Cant make friendship!");
                        }
                        //oRet[i] = String.Empty;
                    }
                    catch (TwitterizerException e1)
                    {
                        oRet.Add(t+": "+e1.Message);
                    }
                    finally
                    {
                        Thread.Sleep(1000);//1sec before we going again!
                    }
                }
            }
            catch (Exception e1)
            {
                oRet.Add(e1.Message);
            }
            return oRet;  
        }

        /// <summary>
        /// Интерфейс по удалению френдов
        /// </summary>
        /// <param name="who">Имя аккаунта для которого меняем френдов</param>
        /// <param name="friends">Набор имен аккаунтов для френдования</param>
        /// <returns>NULL, если все хорошо, иначе ошибку ввиде Exception</returns>
        public override List<String> DelFriends(UserAccount who, params string[] friends)
        {
            var oRet = new List<string>(0);
            try
            {
             /*   Twitterizer.Framework.Twitter tw = new Twitterizer.Framework.Twitter(who.username, who.password, "BlogsAPI");*/
                if (!LogInto(who))
                {
                    throw new Exception("Cant get oauth token!");
                }
                OAuthTokens tokens = Configuration.GetTokens(who.username, DefaultConfig);
                foreach (string t in friends)
                {
                    try
                    {
                        TwitterResponse<TwitterUser> usr = TwitterFriendship.Delete(tokens, t);
                        if (usr == null || usr.Result != RequestResult.Success)
                        {
                            throw new TwitterizerException("Cant delete friendship!");
                        }
                        //oRet[i] = String.Empty;
                    }
                    catch (TwitterizerException e1)
                    {
                        oRet.Add(t + ": " + e1.Message);
                    }
                    finally
                    {
                        Thread.Sleep(1000);//1sec before we going again!
                    }
                }
            }
            catch (Exception e1)
            {
                oRet.Add(e1.Message);
            }
            return oRet;            
        }

        /// <summary>
        /// Функция вступления в сообщество.
        /// </summary>
        /// <param name="who">кто вступает</param>
        /// <param name="communityName">куда вступает</param>
        /// <returns>Ошибку, если таковая имела место</returns>
        public Exception JoinCommunnity(UserAccount who, string communityName)
        {
            return new NotSupportedException("Твиттер не имеет сообществ!");
        }

        /// <summary>
        /// Функция покидания сообщества.
        /// </summary>
        /// <param name="who">кто покидает</param>
        /// <param name="communityName">что покидает</param>
        /// <returns>Ошибку, если таковая имела место</returns>
        public Exception LeaveCommunnity(UserAccount who, string communityName)
        {
            return new NotSupportedException("Твиттер не имеет сообществ!");
        }

        /// <summary>
        /// Получаем список кого зафоловили и расфренживаем всех
        /// </summary>
        /// <param name="who"></param>
        /// <returns></returns>
        public Exception UnfollowAll(UserAccount who)
        {
            Exception oRet = null;
            try
            {
                if (LoggedInAs == null && !LogInto(who))
                {
                    throw new Exception("Ошибка залогинивания!");
                }
                OAuthTokens tokens = Configuration.GetTokens(who.username, DefaultConfig);
                if (FriendList == null) //не подгрузили , так загрузим
                {
                    //FriendsOptions options = new FriendsOptions();
                    //FriendList = TwitterFriendship.Friends(tokens,options).ResponseObject;
                    FriendList =
                        TwitterFriendship.FriendsIds(tokens, new UsersIdsOptions {ScreenName = who.username}).
                            ResponseObject;
                }
                if (FriendList != null)
                {
                    foreach (decimal userid in FriendList)
                    {
                        TwitterResponse<TwitterUser> oResp = TwitterFriendship.Delete(tokens, userid);
                        if (oResp.Result != RequestResult.Success)
                        {
                            oRet = new Exception(oResp.ErrorMessage + " " + oResp.Result.ToString());
                            break;
                        }
                    }
                }
            }catch(Exception e1)
            {
                oRet = e1;
            }
            return oRet;
        }

        #endregion

        #region IQuote

        /// <summary>
        /// Получение ссылки для цитирования по посту
        /// </summary>
        /// <param name="bp">Заполненный объект с постом</param>
        /// <returns>ссылку на страницу цитирования, NULL если цитирование не возможно</returns>
        /// <remarks>http://twitter.com/?status=RT%20%D0%</remarks>
        public String GetQuoteLink(BlogPost bp)
        {
            if (String.IsNullOrEmpty(bp.Body)) return "";
            int maxlen = bp.Link == null ? 137 : 120;
            string text = bp.Body.Length > maxlen ? bp.Body.Substring(0, maxlen) : bp.Body;
            text = "RT " + text.Trim();
            if(bp.Link!=null)
            {
                //делаем ссылку на первоисточник(!)
                BitLy bl = new BitLy();
                text += " "+bl.ConvertDataTo(bp.Link.OriginalString, ItemType.FullUrl, ItemType.ShortUrl);
            }
            return "http://twitter.com/?status=" +
                   HttpUtility.UrlEncode(text);
        }

        /// <summary>
        /// Получение кода для цитирования твита
        /// </summary>
        /// <remarks>
        /// <blockquote class="twitter-tweet"><p><a href="https://twitter.com/search/%2523blog">#blog</a> Kaspersky Tablet Bugreport: Попробую написать в паблик, так как по почте Касперский до сих пор молчит. При... <a href="http://t.co/OLYw2tuK" title="http://bit.ly/Kcg098">bit.ly/Kcg098</a></p>&mdash; Rostislav Топблогер (@f1ashr) <a href="https://twitter.com/f1ashr/status/198329346337939456" data-datetime="2012-05-04T08:32:55+00:00">May 4, 2012</a></blockquote>
        ///</remarks>
        public String GetEmbedHtml(Uri Link)
        {
            string html = "<b>Не удалось загрузить твит</b>";
            if(ValidateLink(Link.OriginalString))
            {
                decimal statusid = GetStatusId(new Uri(Link.OriginalString));
                TwitterResponse<TwitterStatus> trts = TwitterStatus.Show(statusid);
                if(trts.Result == RequestResult.RateLimited)
                {
                    //change from OAuth to client
                    trts = TwitterStatus.Show(statusid);
                }
                if (trts.Result == RequestResult.Success)
                {
                    TwitterStatus ts = trts.ResponseObject;
                    html =
                        String.Format(
                            "<blockquote class=\"twitter-tweet\"><p>{0}</p>&mdash; {1} (@{2}) <a href=\"{3}\" data-datetime=\"{4}\">{5}</a></blockquote>"
                            , ts.LinkifiedText(), ts.User.Name, ts.User.ScreenName, Link.OriginalString,
                            ts.CreatedDate.ToString("s"), ts.CreatedDate.ToString("MM dd, yyyy"));

                }
                html += "<script src=\"//platform.twitter.com/widgets.js\" charset=\"utf-8\"></script>";
            }
            return html;
        }

        #endregion

        #region IData
        /// <summary>
        /// Получение ссылки на rss-блогера 
        /// По умолчанию, грузим блог и извлекаем ссылку
        /// </summary>
        /// <param name="userName">ник блогера</param>
        /// <param name="btype">Тип блога - сообщество или блог</param>
        /// <returns>Link on RSS feed</returns>
        [Obsolete("GetRssLink removed for API v1.1 http://habrahabr.ru/post/150960/#habracut")]
        public override string GetRssLink(string userName, BlogType btype)
        {
            return String.Format("http://twitter.com/statuses/user_timeline/{0}.rss", userName);
            //return "";
        }


        #endregion

        #region ILike

        /// <summary>
        /// Поиск значений лайков и прочего по ссылке
        /// </summary>
        /// <param name="Link">ссылка на страницу</param>
        /// <returns>массив данных</returns>
        /// <remarks>http://urls.api.twitter.com/1/urls/count.json?url={0} - removed at 20.11.2015</remarks>
        public Dictionary<string, int> GetLikes(Uri Link)
        {
            TwitterResponse<TwitterSearchResultCollection> result;
            return GetLikes(Link, out result);
        }

        /// <summary>
        /// Поиск значений лайков и прочего по ссылке
        /// </summary>
        /// <param name="Link">ссылка на страницу</param>
        /// <param name="result">результат</param>
        /// <returns>массив данных</returns>
        /// <remarks>http://urls.api.twitter.com/1/urls/count.json?url={0} - removed at 20.11.2015</remarks>
        public Dictionary<string, int> GetLikes(Uri Link, out TwitterResponse<TwitterSearchResultCollection> result)
        {
            var oRet = new Dictionary<string, int>();
            result = null;
            try
            {
                if (LoggedInAs == null && !LogInto(oSelf))
                {
                    return oRet;
                }
                OAuthTokens tokens = Configuration.GetTokens(LoggedInAs.username, DefaultConfig);
                TwitterResponse<TwitterSearchResultCollection> res = null;
                var total = 0;
                do
                {
                    res = TwitterSearch.Search(tokens,
                        Common.DelHttp(Link.OriginalString),
                        new SearchOptions
                        { UseSSL = true,
                            Count = 100,
                            IncludeEntities = false,
                            MaxId = res?.ResponseObject.Last().Id ?? 0}
                        );
                    if (res.Result == RequestResult.Success)
                    {
                        total += res.ResponseObject.Count;
                    }
                } while (res.Result == RequestResult.Success && res.ResponseObject.Count == 100 && total < 1000);//ограничиваемся 1000, иначе много запросов

                //все прошло на ура, иначе не будем добавлять даже 0, или у нас есть что-то найденное
                if (res.Result == RequestResult.Success || total > 0)
                {
                    oRet.Add("count", total);
                }
                result = res;
            }
            catch (Exception e1)
            {
                Debug.WriteLine(e1);
            }
            return oRet;
        }

        #endregion
    }
}